<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>将div作为input,解决input显示表情</title>
  </head>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .app {
      width: 400px;
      height: 700px;
      border: 1px solid #000;
      display: flex;
      flex-direction: column;
    }

    .app .content {
      flex: 1;
    }

    .app .input {
      height: 150px;
      border: 1px solid #000;
      margin: 4px;
      padding: 10px;
      outline: none;
    }
    .app .input:empty::before {
      content: attr(placeholder);
      color: #bbb;
    }

    .input img {
      width: 20px;
      height: 20px;
      vertical-align: bottom;
    }

    .left {
      width: 100px;
      height: 100px;
      position: fixed;
      left: 20px;
      border: 1px solid #000;
      right: 100px;
    }

    .left img {
      width: 40px;
      height: 40px;
      cursor: pointer;
    }
  </style>

  <body>
    <div class="app">
      <!-- <div class="content"></div> -->
      <!-- <div>
        <button>表情</button>
        <button>字体</button>
      </div> -->
      <div class="input" id="input" contenteditable="true" placeholder="自定义input，请输入内容"></div>
    </div>
    <div class="left" id="left">
      <img src="./emoji.gif" alt="" />
      <!-- <img src="https://i-blog.csdnimg.cn/blog_migrate/30375aa6cd6be0d855464f450560b6a6.gif#pic_center" alt="" />
      <img src="https://i-blog.csdnimg.cn/blog_migrate/1031101ced474396827f234eab40d57f.gif#pic_center" alt="" />
      <img src="https://i-blog.csdnimg.cn/blog_migrate/889e11dda257d52354afb7a92ada4b03.gif#pic_center" alt="" />
      <img src="https://i-blog.csdnimg.cn/blog_migrate/4f9f71c8c11813a68abee7f0f87d4bef.gif#pic_center" alt="" /> -->
    </div>
  </body>
</html>
<script>
  var lastEditRange;
  const input = document.getElementById('input');

  // 编辑框点击事件
  input.onclick = e => {
    const childNodes = Array.from(input.childNodes);
    const selection = getSelection();
    const { focusNode } = selection;
    // 点击图片
    if (!e.target.innerHTML) {
      selection.removeAllRanges();
      const index = childNodes.findIndex(v => v === e.target);
      selection.collapse(input, index === -1 ? childNodes.length : index);
    }
    lastEditRange = selection.getRangeAt(0);
  };
  input.onkeyup = () => {
    var selection = getSelection();
    lastEditRange = selection.getRangeAt(0); // 第一个选区
  };

  const left = document.getElementById('left');
  left.onclick = e => {
    var selection = getSelection();
    selection.removeAllRanges();
    selection.addRange(lastEditRange);

    let input = selection.anchorNode;

    console.log(`%c input-from-div.html 105 `,'background:aqua;', input);
    
    const childNodes = Array.from(input.childNodes);
   
    // div.innerHTML += `<img src="${e.target.src}" />`
    var range = selection.getRangeAt(0);
    // 获取光标位置
    var rangeStartOffset = range.startOffset;
    
    var img = document.createElement('img');
    img.src = e.target.src;


  
    console.log('光标位置', rangeStartOffset);
    console.log('是否编辑框（还是文本节点）', selection.anchorNode.nodeName != '#text');
    console.log('光标之前的节点个数',selection.anchorOffset);
    
    
    // 判断选定对象范围是编辑框还是文本节点
    if (selection.anchorNode.nodeName != '#text') {
      console.log('input 子元素长度', input.childNodes.length);
    
      // 如果文本框的子元素大于0，则表示有其他元素，则按照位置插入表情节点
      for (var i = 0; i <= input.childNodes.length; i++) { // 
        if (i == selection.anchorOffset) {
          input.insertBefore(img, input.childNodes[i]); // 不应该插入到 input，仅在首行可用
        }
      }
      // 创建新的光标对象
      var range = document.createRange();
      // 光标对象的范围界定为新建的表情节点
      range.selectNodeContents(img);
      // 光标位置定位在表情节点的最大长度
      range.setStart(input, Array.from(input.childNodes).findIndex(v => v === img) + 1);
      // 使光标开始和光标结束重叠
      range.collapse(true);
      // 清除选定对象的所有光标对象
      selection.removeAllRanges();
      // 插入新的光标对象
      selection.addRange(range);
    } else {
      // 如果是文本节点则先获取光标对象
      // 获取光标对象的范围界定对象，一般就是textNode对象
      var textNode = range.startContainer;
      // 获取光标位置
      const { textContent } = textNode;
      // 分割开始值
      const startStr = textContent.substr(0, rangeStartOffset);
      // 分割结束值
      const endStr = textContent.substr(rangeStartOffset);
      // 当前节点的索引
      const index = childNodes.findIndex(v => v === textNode);
      // 移除当前文本节点
      const startStrNode = document.createTextNode(startStr);
      const endStrNode = document.createTextNode(endStr);

      if(!(input instanceof HTMLElement)){
        input = input.parentElement;
      }
      // input.removeChild?.(textNode);
      if (index === childNodes.length - 1) {
        // 最后一个节点处理
        input.appendChild(startStrNode);
        input.appendChild(img);
        input.appendChild(endStrNode);
        selection.collapse(input, index + 2);
      } else {
        // 不是最后一个
        input.insertBefore(startStrNode, childNodes[index + 1]);
        input.insertBefore(img, childNodes[index + 1]);
        input.insertBefore(endStrNode, childNodes[index + 1]);
        selection.collapse(input, Array.from(input.childNodes).findIndex(v => v === img) + 1);
      }

      // 光标开始和光标结束重叠
      range.collapse(true);
      // // 清除选定对象的所有光标对象
      // selection.removeAllRanges()
      // // 插入新的光标对象
      selection.addRange(range);
    }
    // 无论如何都要记录最后光标对象
    lastEditRange = selection.getRangeAt(0);
  };
</script>
